﻿

using DotNetty.Buffers;
using Google.Protobuf;
using QQ.Codecs;
using QQ.Packet;
using QQ.Packet.Request;
using QQTars;
using System;
using System.Threading.Tasks;
using Tars.Net.Codecs;
using Tars.Net.Hosting;
using Tars.Net.Metadata;
using static System.Console;

namespace QQ.Client
{
    public class QQController
    {
        private uint messageSequenceId = 22911;
        private uint MessageSequenceId
        {
            get
            {
                return messageSequenceId += 2;
            }
            set { }
        }
        private readonly QQTcpClient client;
        private readonly QQDeviceConfig config;
        private readonly OICQRequest oicqRequest;
        private readonly SSOFrame ssoFrame;
        private readonly HeadPacket headPacket;
        private readonly ClientCallback clientCallback;
        private readonly PushService pushService;
        private readonly LoginSigInfo loginSigInfo;
        private readonly UniRequest uniRequest;
        private readonly IServerHandler serverHandler;
        private readonly IEncoder<IByteBuffer> encoder;

        public QQController(IEncoder<IByteBuffer> encoder, IServerHandler serverHandler,UniRequest uniRequest, LoginSigInfo loginSigInfo, PushService pushService, ClientCallback clientCallback, HeadPacket headPacket, SSOFrame ssoFrame, OICQRequest oicqRequest, QQDeviceConfig config, QQTcpClient client)
        {
            this.client = client;
            this.headPacket = headPacket;
            this.ssoFrame = ssoFrame;
            this.oicqRequest = oicqRequest;
            this.config = config;
            this.pushService = pushService;
            this.loginSigInfo = loginSigInfo;
            this.clientCallback = clientCallback;
            this.clientCallback.wtlogin_callback = wtlogin_callback;
            this.clientCallback.PbPushMsg_callback = PbPushMsg_callback;
            this.clientCallback.HeartBeatTimeOut = HeartBeatTimeOut;
            this.clientCallback.DisconnectByServer = DisconnectByServer;
            this.clientCallback.request_callback = request_callback;
            this.uniRequest = uniRequest;
            this.serverHandler = serverHandler;
            this.encoder = encoder;
            Login(loginSigInfo.uin,loginSigInfo.password).Wait();
        }

        private async void request_callback(Request obj)
        {
            var resp = serverHandler.Process(obj);

            headPacket.encryptType = HeadPacket.EncryptType.D2KEY;
            headPacket.body = encoder.EncodeResponse(resp);
            headPacket.packetType = 0x0A;
            await client.SendAsync(headPacket.Serialize());
        }

        private void wtlogin_callback()
        {
            WriteLine("一次登录成功回调。");
            StatSvcRegister();
        }

        private async void PbPushMsg_callback(Qqprotobuf.OnlinePush.PbPushMsg msg)
        {
            string groupName = msg.Msg.MsgHead.GroupInfo.GroupName.ToStringUtf8();
            ulong groupCode = msg.Msg.MsgHead.GroupInfo.GroupCode;
            string content = msg.Msg.MsgBody.RichText.Elems?[0]?.Text?.Str;
            string nick = msg.Msg.MsgHead.GroupInfo.GroupCard;
            ulong uin = msg.Msg.MsgHead.FromUin;
            WriteLine($"[群消息]\n群名称：{groupName}\n群号：{groupCode}\n昵称：{nick}\nUIN：{uin}\n消息内容：{content}");
            if (uin != loginSigInfo.uin && content != "") await SendMessage(1, groupCode, content + "\n来自未来通讯系统。");
        }

        public async Task Login(uint uin, string password)
        {
            LoginRequest loginRequest = new LoginRequest(config)
            {
                uin = uin,
                password = password,
            };
            oicqRequest.cmd = 0x810;
            oicqRequest.uin = (int)uin;
            oicqRequest.PlainBody = loginRequest.Serialize();

            ssoFrame.cmdName = "wtlogin.login";
            ssoFrame.body = oicqRequest.Serialize();

            headPacket.encryptType = Packet.HeadPacket.EncryptType.ZERO;
            headPacket.body = ssoFrame.Serialize();
            headPacket.packetType = 0x0A;
            await client.SendAsync(headPacket.Serialize());
        }

        public async Task SendMessage(byte type, ulong uin, string msg)
        {
            Qqprotobuf.MsgSvc.PbSendMsgReq pbSend = new Qqprotobuf.MsgSvc.PbSendMsgReq()
            {
                RoutingHead = new Qqprotobuf.MsgSvc.RoutingHead()
                {
                    Grp = new Qqprotobuf.MsgSvc.Grp()
                    {
                        GroupCode = uin,
                    }
                },
                ContentHead = new Qqprotobuf.MsgComm.ContentHead()
                {
                    PkgNum = 1
                },
                MsgBody = new Qqprotobuf.ImMsgBody.MsgBody()
                {
                    RichText = new Qqprotobuf.ImMsgBody.RichText()
                },
                MultiSendSeq = MessageSequenceId,
                MsgRand = (uint)Utils.MiscHelper.getRandomNumber(),
                SyncCookie = ByteString.CopyFromUtf8(""),
                MsgVia = 1
            };
            pbSend.MsgBody.RichText.Elems.Add(new Qqprotobuf.ImMsgBody.Elem()
            {
                Text = new Qqprotobuf.ImMsgBody.TextProto()
                {
                    Str = msg
                }
            });
            pbSend.MsgBody.RichText.Elems.Add(new Qqprotobuf.ImMsgBody.Elem()
            {
                GeneralFlags = new Qqprotobuf.ImMsgBody.GeneralFlags()
                {
                    PbReserve = ByteString.CopyFrom("78 00 F8 01 00 C8 02 00".HexStringToBytes())
                }
            });

            uniRequest.body = pbSend.ToByteArray().ToByteBuffer();
            uniRequest.cmdName = "MessageSvc.PbSendMsg";

            headPacket.encryptType = HeadPacket.EncryptType.D2KEY;
            headPacket.body = uniRequest.Serialize();
            headPacket.packetType = 0x0B;
            await client.SendAsync(headPacket.Serialize());
        }

        public void StatSvcRegister()
        {
            Qqprotobuf.Oidb.Oidb0x769 oidb0X769 = new Qqprotobuf.Oidb.Oidb0x769();
            oidb0X769.RptConfigList.Add(new Qqprotobuf.Oidb.ConfigSeq()
            {
                Type = 46,
                Version = 0
            });
            oidb0X769.RptConfigList.Add(new Qqprotobuf.Oidb.ConfigSeq()
            {
                Type = 283,
                Version = 0
            });

            SvcReqRegister svcReqRegister = new SvcReqRegister()
            {
                cConnType = 0,
                lBid = 1 | 2 | 4,
                lUin = loginSigInfo.uin,
                iStatus = (int)OnlineStatus.ONLINE,
                bKikPC = 0,
                bKikWeak = 0,
                timeStamp = 0,
                iLargeSeq = 1551,
                bOpenPush = 1,
                iLocaleID = 2052,
                bRegType = 0,//TODO MORE TYPE FILL IT
                bIsSetStatus = 0,//TODO
                iOSVersion = config.sdk,
                cNetType = 1,//TODO WIFI
                vecGuid = config.GUID,
                strDevName = config.model,
                strDevType = config.model,
                strOSVer = config.release,
                uOldSSOIp = 0,
                uNewSSOIp = 281474976710783,//00 01 00 01 00 00 00 7F
                strVendorName = config.VendorName,
                strVendorOSName = config.VendorOSName,
                bytes_0x769_reqbody = oidb0X769.ToByteArray().ToByteBuffer(),
                bSetMute = 0
            };
            pushService.SvcReqRegister(svcReqRegister);
        }

        public async void HeartBeatTimeOut()
        {
            await HeartBeat();
        }

        public async Task HeartBeat()
        {
            ssoFrame.cmdName = "Heartbeat.Alive";
            ssoFrame.body = Unpooled.Buffer();

            headPacket.encryptType = HeadPacket.EncryptType.NULL;
            headPacket.body = ssoFrame.Serialize();
            headPacket.packetType = 0x0A;
            await client.SendAsync(headPacket.Serialize());
        }

        public void DisconnectByServer()
        {
            client.Reconnect();
        }
    }
}
